﻿using System;
using System.Linq;
using System.Windows;
using Tanis.RouteService;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Data;
using Microsoft.Phone.Controls;
using System.Threading;
using Microsoft.Phone.Reactive;
using System.Text.RegularExpressions;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Controls;
using Microsoft.Phone.Shell;
using Tanis.Settings;

namespace Tanis.Controls
{
    public partial class RouteFinderControl
    {
        public RouteFinderControl()
		{
			InitializeComponent();
			App.ServiceManager.StopListLoaded +=OnStopsCompleted;
            
            _startTextBox.ItemFilter += SearchRouteSuggestion;
            _destTextBox.ItemFilter += SearchRouteSuggestion;
		}

        #region AutoCompleteBox functions

        private bool SearchRouteSuggestion(string search, object value)
        {
            string route = value as string;
            if (route != null)
            {
                return route.RemoveDiacritics().ToLower().Contains(search.ToLower());
            }
  
            return false;
        }

        private void autoCompleteBox_Populating(object sender, PopulatingEventArgs e)
        {
            e.Cancel = true;
            BackgroundWorker autoCompleteWorker = new BackgroundWorker()
            {
                WorkerSupportsCancellation = true
            };
            autoCompleteWorker.DoWork += new DoWorkEventHandler(autoCompleteWorker_DoWork);
            autoCompleteWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(autoCompleteWorker_RunWorkerCompleted);

            if (!autoCompleteWorker.IsBusy)
            {
                autoCompleteWorker.RunWorkerAsync(new AutoCompleteworkerParameter()
                {
                    AutoCompleteBox = sender as AutoCompleteBox,
                    CurrentText = e.Parameter
                });
            }
        }

        private void autoCompleteWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            AutoCompleteworkerParameter param = (AutoCompleteworkerParameter)e.Argument;
            if (itemsSource != null)
            {
                param.CurrentText = param.CurrentText.ToLower();
                param.Result = new ObservableCollection<string>(itemsSource.Where(candidate => 
                    candidate.RemoveDiacritics().ToLower().Contains(param.CurrentText)));
                e.Result = param;
            }
        }

        private void autoCompleteWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            AutoCompleteworkerParameter param = e.Result as AutoCompleteworkerParameter;
            if (param != null)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    param.AutoCompleteBox.ItemsSource = (ObservableCollection<string>)param.Result;
                    
                    param.AutoCompleteBox.PopulateComplete();
                });
            }
        }

        private void OnAutoCompleteBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                FixFocus(1);
            }
        }

        private void OnAutoCompleteBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            if (((AutoCompleteBox) sender).SelectedItem != null)
            {
                FixFocus(4);
            }
        }

        private int _refocusCount;

        /// <summary>
        /// For some strange reason AutoCompleteBox.GotFocus/LostFocus is called
        /// 1 additional time for KeyDown and 4 times for SelectionChanged
        /// TODO: Investigate and fix to avoid this hack
        /// </summary>
        /// <param name="refocusCount">Number of times to ignore GotFocus/LostFocus events</param>
        private void FixFocus(int refocusCount)
        {
            _refocusCount = refocusCount;
            RestoreControls();
            _dateCtrl.Focus();
        }        

        private void OnAutoCompleteBox_GotFocus(object sender, EventArgs e)
        {
            if (_refocusCount > 0)
            {
                _refocusCount--;
                _dateCtrl.Focus();
                return;
            }

            FocusOnControl(((AutoCompleteBox) sender).Tag as string);
        }

        private void OnAutoCompleteBox_LostFocus(object sender, EventArgs e)
        {
            if (_refocusCount > 0)
            {
                return;
            }

            RestoreControls();
        }

        private void FocusOnControl(string tag)
        {
            int marginOffset = -156;
            if (tag == "arrival")
            {
                marginOffset = -114;
                if (validStart.Visibility == System.Windows.Visibility.Visible)
                {
                    marginOffset += 30;
                }
            }
            Pivot current = ((PivotItem)this.Parent).Parent as Pivot;
            current.Margin = new Thickness(0, marginOffset, 0, 0);

            var elementsToHide = LayoutRoot.Children.Cast<FrameworkElement>().Where(x => x.Tag as string != tag && x.Visibility == System.Windows.Visibility.Visible);

            foreach (var element in elementsToHide)
            {
                element.Visibility = System.Windows.Visibility.Collapsed;
            }
        }

        private void RestoreControls()
        {
            var elementsToRestore = LayoutRoot.Children.Cast<FrameworkElement>().Where(x => x.Tag as string != "validator" && x.Visibility == System.Windows.Visibility.Collapsed);

            foreach (var element in elementsToRestore)
            {
                element.Visibility = System.Windows.Visibility.Visible;
            }

            Pivot current = ((PivotItem)this.Parent).Parent as Pivot;
            current.Margin = new Thickness(0, 0, 0, 0);
        }

        private void OnStopsCompleted(object sender, GetStopNamesCompletedEventArgs args)
        {
            itemsSource = new ObservableCollection<string>(args.Result.Select(x => x).Distinct().ToList());
        }

        #endregion

	    #region Parameters

        private ObservableCollection<string> itemsSource = null;

		private string CityName
		{
            get { return SettingsControl.DefaultCity; }
		}

		private string StartPlace
		{
            get { return _startTextBox.Text; }
		}

		private string DestinationPlace
		{
            get { return _destTextBox.Text; }
		}

		private DateTime DateAndTime
		{
			get
			{
				if (_dateCtrl.Value.HasValue && _timeCtrl.Value.HasValue)
				{
					DateTime date = _dateCtrl.Value.Value;
					DateTime time = _timeCtrl.Value.Value;
					var dateTime = new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second);
					
					return dateTime;
				}

				return DateTime.Now;
			}
		}

        private bool _isArrival;
		private bool IsArrival {
            get
            {
                return _isArrival;
            }
            set
            {
                _isArrival = value;

                TextBlock selectedLabel, deselectedLabel;

                if (value)
                {
                    selectedLabel = _arrivalSelected;
                    deselectedLabel = _departureSelected;
                }
                else
                {
                    selectedLabel = _departureSelected;
                    deselectedLabel = _arrivalSelected;
                }


                deselectedLabel.TextDecorations = TextDecorations.Underline;
                deselectedLabel.Foreground = new SolidColorBrush(Colors.Blue);

                selectedLabel.TextDecorations = null;
                selectedLabel.Foreground = new SolidColorBrush(Colors.White);
            }
        }

        public bool SearchPerformed { get; set; }
        public bool FavouriteSearchPerformed { get; set; }
        public bool RouteDetailsRequested { get; set; }
		
		#endregion

        #region search handlers
        public void OnSearchBtnClick(object sender, EventArgs e)
		{
            if (ValidateInput())
            {
                App.ViewModel.OnSearchPerfoming();
                FavouriteSearchPerformed = false;
                PerformSearch();
                App.ViewModel.OnSearchPerfomed();                    
            }
		    {
                if (String.IsNullOrEmpty(StartPlace))
                {
                    validStart.Visibility = Visibility.Visible;
                }
                if (String.IsNullOrEmpty(DestinationPlace))
                {
                    validDest.Visibility = Visibility.Visible;
                }
		    }
		}

		public void PerformSearch()
		{
			App.ServiceManager.GetRoutes(CityName, StartPlace, DestinationPlace, DateAndTime, IsArrival);
            SearchPerformed = true;
            RouteDetailsRequested = false;
            MainPage.MainPageInstance.UpdatePanelVisibility();
		}

        private bool ValidateInput()
        {
            return StartPlace != String.Empty && DestinationPlace != String.Empty;
        }

        public RouteQuery GetRouteQuery()
        {
            return new RouteQuery(CityName, StartPlace, DestinationPlace);
        }

        public void SetRouteQuery(RouteQuery route)
        {
            _startTextBox.Text = route.StartPlace;
            _destTextBox.Text = route.DestinationPlace;
            _dateCtrl.Value = DateTime.Now;
            _timeCtrl.Value = DateTime.Now;
        }
        #endregion

        #region other handlers
        private void _departureSelected_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            IsArrival = false;
        }

        private void _arrivalSelected_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            IsArrival = true;
        }
        #endregion
    }

    public class RouteQuery
    {
        public RouteQuery()
        {
        }

        public RouteQuery(string cityName, string startPlace, string destinationPlace)
        {
            CityName = cityName;
            StartPlace = startPlace;
            DestinationPlace = destinationPlace;
        }

        public string DefaultName
        {
            get
            {
                return DestinationPlace;
            }
        }

        public string Name { get; set;}
        public string CityName { get; set;}
        public string StartPlace { get; set; }
        public string DestinationPlace { get; set; }
    }
}
